package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author 饭勺
 * @description
 * @createDate 2025/10/14 17:19
 */
@Slf4j
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;


    @Override
    public String getGoods(Long id) throws InterruptedException {
        //1.优先从缓存中获取商品信息
        String key = "goods:" + id;
        String goodsInfo = (String) redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(goodsInfo)) {
            log.info("线程：{}， 商品信息:{}已从缓存中获取！", Thread.currentThread().getId(), id);
            return goodsInfo;
        }
        log.info("线程：{}， 商品信息:{}未从缓存中获取！", Thread.currentThread().getId(), id);

        //2.利用分布式锁解决缓存击穿造成数据库压力，避免大量请求同时查询数据库
        //2.1 创建锁对象 不同商品ID创建不同的锁对象，不互斥
        RLock lock = redissonClient.getLock("goods:lock:" + id);
        //2.2 获取锁
        boolean flag = lock.tryLock();
        //2.3 获取锁成功，查询数据库,将查询结果放入缓存，释放锁
        if (flag) {
            try {
                log.info("线程：{}， 获取锁成功", Thread.currentThread().getId());
                log.info("线程：{}， 查询数据库,", Thread.currentThread().getId());
                redisTemplate.opsForValue().set(key, "商品信息：" + id, 30, TimeUnit.MINUTES);
                return "商品信息：" + id;
            } finally {
                //释放锁
                log.info("线程：{}， 释放锁", Thread.currentThread().getId());
                lock.unlock();
            }
        } else {
            //3.获取锁失败，休眠后自旋
            TimeUnit.MILLISECONDS.sleep(100);
            log.info("线程：{}， 获取锁失败，休眠后自旋", Thread.currentThread().getId());
            return this.getGoods(id);
        }
    }
}

